package types

import (
	"encoding/xml"
	"errors"
)

const (
	// xml 标准头
	XMLHeader = `<?xml version="1.0" encoding="UTF-8"?>` + "\n"
	// obix 头
	XMLStylesheet = `<?xml-stylesheet type='text/xsl' href='/obix/xsl'?>` + "\n"
	// 命名空间
	XMLNamespace = `http://obix.org/ns/schema/1.0`
)

// Obj 是和服务器交互时进行 XML 序列化和反序列化使用的数据类型
// 每个点位返回的结果对应着一个数据类型, 可以使用Obj.AssertXxx进行断言
// 如: AssertBool/AssertErr
type Obj struct {
	XMLName xml.Name   `json:"-"`
	Type    ObjectType `xml:"-" `
	// Object的具体类型包含:
	//	- Object
	//	- Bool
	//	- Str
	//	- Ref
	//	- Real
	//	- RelTime
	//	- AbsTime
	//	- Date
	//	- Err
	//	- Feed
	//	- List
	//	- Uri
	//	- Op
	//	- Int
	//	- Enum
	//	- Time
	Object XMLObject `xml:"-"`
}

// NewObj 创建新的Obj, 在发送数据时, 使用该函数可以生成符合xml序列化的的数据类型
// typ指定的类型应当和v是同一个类型, 但实现上并没有判断
// opts参数目前可以指定xml命名空间, 如: WithNamespace(XMLNamespace)
func NewObj(typ ObjectType, v XMLObject, opts ...ObjOptions) *Obj {
	o := &Obj{
		XMLName: xml.Name{
			Local: string(typ),
		},
		Type:   typ,
		Object: v,
	}
	for _, opt := range opts {
		if opt != nil {
			opt(o)
		}
	}
	return o
}

func (o *Obj) IsErr() bool {
	return o.Type == ObjectTypeErr
}

// AssertObject 断言Object
func (o *Obj) AssertObject() *Object {
	if o.Type == ObjectTypeObj {
		obj, ok := o.Object.(*Object)
		if ok {
			return obj
		}
	}
	return nil
}

// AssertBool 断言Bool
func (o *Obj) AssertBool() *Bool {
	if o.Type == ObjectTypeBool {
		obj, ok := o.Object.(*Bool)
		if ok {
			return obj
		}
	}
	return nil
}

// AssertStr 断言Str
func (o *Obj) AssertStr() *Str {
	if o.Type == ObjectTypeStr {
		obj, ok := o.Object.(*Str)
		if ok {
			return obj
		}
	}
	return nil
}

// AssertRef 断言Ref
func (o *Obj) AssertRef() *Ref {
	if o.Type == ObjectTypeRef {
		obj, ok := o.Object.(*Ref)
		if ok {
			return obj
		}
	}
	return nil
}

// AssertReal 断言Real
func (o *Obj) AssertReal() *Real {
	if o.Type == ObjectTypeReal {
		obj, ok := o.Object.(*Real)
		if ok {
			return obj
		}
	}
	return nil
}

// AssertRelTime 断言RelTime
func (o *Obj) AssertRelTime() *RelTime {
	if o.Type == ObjectTypeRelTime {
		obj, ok := o.Object.(*RelTime)
		if ok {
			return obj
		}
	}
	return nil
}

// AssertAbsTime 断言AbsTime
func (o *Obj) AssertAbsTime() *AbsTime {
	if o.Type == ObjectTypeAbsTime {
		obj, ok := o.Object.(*AbsTime)
		if ok {
			return obj
		}
	}
	return nil
}

// AssertBool 断言Bool
func (o *Obj) AssertDate() *Date {
	if o.Type == ObjectTypeDate {
		obj, ok := o.Object.(*Date)
		if ok {
			return obj
		}
	}
	return nil
}

// AssertBool 断言Bool
func (o *Obj) AssertErr() *Err {
	if o.Type == ObjectTypeErr {
		obj, ok := o.Object.(*Err)
		if ok {
			return obj
		}
	}
	return nil
}

// AssertFeed 断言Feed
func (o *Obj) AssertFeed() *Feed {
	if o.Type == ObjectTypeFeed {
		obj, ok := o.Object.(*Feed)
		if ok {
			return obj
		}
	}
	return nil
}

// AssertList 断言List
func (o *Obj) AssertList() *List {
	if o.Type == ObjectTypeList {
		obj, ok := o.Object.(*List)
		if ok {
			return obj
		}
	}
	return nil
}

// AssertUri 断言Uri
func (o *Obj) AssertUri() *Uri {
	if o.Type == ObjectTypeUri {
		obj, ok := o.Object.(*Uri)
		if ok {
			return obj
		}
	}
	return nil
}

// AssertOp 断言Op
func (o *Obj) AssertOp() *Op {
	if o.Type == ObjectTypeOp {
		obj, ok := o.Object.(*Op)
		if ok {
			return obj
		}
	}
	return nil
}

// AssertInt 断言Int
func (o *Obj) AssertInt() *Int {
	if o.Type == ObjectTypeInt {
		obj, ok := o.Object.(*Int)
		if ok {
			return obj
		}
	}
	return nil
}

// AssertEnum 断言Enum
func (o *Obj) AssertEnum() *Enum {
	if o.Type == ObjectTypeEnum {
		obj, ok := o.Object.(*Enum)
		if ok {
			return obj
		}
	}
	return nil
}

// AssertTime 断言Time
func (o *Obj) AssertTime() *Time {
	if o.Type == ObjectTypeTime {
		obj, ok := o.Object.(*Time)
		if ok {
			return obj
		}
	}
	return nil
}

// Object obix的对象基类, 一般不直接使用, 而是使用 Bool, Str etc...
type Object struct {
	XMLName xml.Name `json:"-"`
	// Xmlns             string `xml:"xmlns,attr,omitempty"`
	XsiSchemaLocation string `xml:"xsi:schemaLocation,attr,omitempty" json:",omitempty"`
	XmlnsXsi          string `xml:"xmlns:xsi,attr,omitempty" json:",omitempty"`
	SchemaLocation    string `xml:"schemaLocation,attr,omitempty" json:",omitempty"`
	Xsi               string `xml:"xsi,attr,omitempty" json:",omitempty"`
	Name              string `xml:"name,attr,omitempty" json:",omitempty"`
	// Href is URI
	Href string `xml:"href,attr,omitempty" json:",omitempty"`
	Is   string `xml:"is,attr,omitempty" json:",omitempty"`
	// Icon is URI
	Icon        string `xml:"icon,attr,omitempty" json:",omitempty"`
	Display     string `xml:"display,attr,omitempty" json:",omitempty"`
	DisplayName string `xml:"displayName,attr,omitempty" json:",omitempty"`
	Status      string `xml:"status,attr,omitempty" json:",omitempty"`
	Writable    bool   `xml:"writable,attr,omitempty" json:",omitempty"`
	Null        bool   `xml:"null,attr,omitempty" json:",omitempty"`

	// 子元素list
	Objects  []*Object  `xml:"obj,omitempty" json:",omitempty"`
	AbsTimes []*AbsTime `xml:"abstime,omitempty" json:",omitempty"`
	Bools    []*Bool    `xml:"bool,omitempty" json:",omitempty"`
	Dates    []*Date    `xml:"date,omitempty" json:",omitempty"`
	Enums    []*Enum    `xml:"enum,omitempty" json:",omitempty"`
	Errs     []*Err     `xml:"err,omitempty" json:",omitempty"`
	Feeds    []*Feed    `xml:"feed,omitempty" json:",omitempty"`
	Ints     []*Int     `xml:"int,omitempty" json:",omitempty"`
	Lists    []*List    `xml:"list,omitempty" json:",omitempty"`
	Ops      []*Op      `xml:"op,omitempty" json:",omitempty"`
	Reals    []*Real    `xml:"real,omitempty" json:",omitempty"`
	Refs     []*Ref     `xml:"ref,omitempty" json:",omitempty"`
	RelTimes []*RelTime `xml:"reltime,omitempty" json:",omitempty"`
	Strs     []*Str     `xml:"str,omitempty" json:",omitempty"`
	Uris     []*Uri     `xml:"uri,omitempty" json:",omitempty"`
	Times    []*Time    `xml:"time,omitempty" json:",omitempty"`
}

type XMLObject interface {
	// 替换属性方法
	ReplaceAttr()
}

// ReplaceAttr 替换部分参数以便生成包含命名空间的属性
func (o *Object) ReplaceAttr() {
	if o == nil {
		return
	}
	o.XsiSchemaLocation = o.SchemaLocation
	o.SchemaLocation = ""
	o.XmlnsXsi = o.Xsi
	o.Xsi = ""
	for _, v := range o.Objects {
		v.ReplaceAttr()
	}
}

// GetObject 获取子元素中指定的Object
func (o *Object) GetObject(name string) *Object {
	for _, v := range o.Objects {
		if v.Name == name {
			return v
		}
	}
	return nil
}

// GetBool 获取子元素中指定的Bool
func (o *Object) GetBool(name string) *Bool {
	for _, v := range o.Bools {
		if v.Name == name {
			return v
		}
	}
	return nil
}

// GetStr 获取子元素中指定的Str
func (o *Object) GetStr(name string) *Str {
	for _, v := range o.Strs {
		if v.Name == name {
			return v
		}
	}
	return nil
}

// GetRef 获取子元素中指定的Ref
func (o *Object) GetRef(name string) *Ref {
	for _, v := range o.Refs {
		if v.Name == name {
			return v
		}
	}
	return nil
}

// GetReal 获取子元素中指定的Real
func (o *Object) GetReal(name string) *Real {
	for _, v := range o.Reals {
		if v.Name == name {
			return v
		}
	}
	return nil
}

// GetRelTime 获取子元素中指定的Reltime
func (o *Object) GetRelTime(name string) *RelTime {
	for _, v := range o.RelTimes {
		if v.Name == name {
			return v
		}
	}
	return nil
}

// GetAbsTime 获取子元素中指定的AbsTime
func (o *Object) GetAbsTime(name string) *AbsTime {
	for _, v := range o.AbsTimes {
		if v.Name == name {
			return v
		}
	}
	return nil
}

// GetDate 获取子元素中指定的Date
func (o *Object) GetDate(name string) *Date {
	for _, v := range o.Dates {
		if v.Name == name {
			return v
		}
	}
	return nil
}

// GetErr 获取子元素中指定的Err
func (o *Object) GetErr(name string) *Err {
	for _, v := range o.Errs {
		if v.Name == name {
			return v
		}
	}
	return nil
}

// GetFeed 获取子元素中指定的Feed
func (o *Object) GetFeed(name string) *Feed {
	for _, v := range o.Feeds {
		if v.Name == name {
			return v
		}
	}
	return nil
}

// GetList 获取子元素中指定的List
func (o *Object) GetList(name string) *List {
	for _, v := range o.Lists {
		if v.Name == name {
			return v
		}
	}
	return nil
}

// GetUri 获取子元素中指定的Uri
func (o *Object) GetUri(name string) *Uri {
	for _, v := range o.Uris {
		if v.Name == name {
			return v
		}
	}
	return nil
}

// GetOp 获取子元素中指定的Op
func (o *Object) GetOp(name string) *Op {
	for _, v := range o.Ops {
		if v.Name == name {
			return v
		}
	}
	return nil
}

// GetInt 获取子元素中指定的Int
func (o *Object) GetInt(name string) *Int {
	for _, v := range o.Ints {
		if v.Name == name {
			return v
		}
	}
	return nil
}

// GetEnum 获取子元素中指定的Enum
func (o *Object) GetEnum(name string) *Enum {
	for _, v := range o.Enums {
		if v.Name == name {
			return v
		}
	}
	return nil
}

// GetTime 获取子元素中指定的Time
func (o *Object) GetTime(name string) *Time {
	for _, v := range o.Times {
		if v.Name == name {
			return v
		}
	}
	return nil
}

// UnmarshalXML 实现xml反序列化
func (o *Obj) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	obj := &Object{
		XMLName: start.Name,
	}
	ot := ObjectType(start.Name.Local)
	switch ot {
	case ObjectTypeObj:
		o.Object = obj
	case ObjectTypeBool:
		o.Object = &Bool{Object: obj}
	case ObjectTypeStr:
		o.Object = &Str{Object: obj}
	case ObjectTypeRef:
		o.Object = &Ref{Object: obj}
	case ObjectTypeReal:
		o.Object = &Real{Object: obj}
	case ObjectTypeRelTime:
		o.Object = &RelTime{Object: obj}
	case ObjectTypeAbsTime:
		o.Object = &AbsTime{Object: obj}
	case ObjectTypeDate:
		o.Object = &Date{Object: obj}
	case ObjectTypeUri:
		o.Object = &Uri{Object: obj}
	case ObjectTypeList:
		o.Object = &List{Object: obj}
	case ObjectTypeErr:
		o.Object = &Err{Object: obj}
	case ObjectTypeOp:
		o.Object = &Op{Object: obj}
	case ObjectTypeFeed:
		o.Object = &Feed{Object: obj}
	case ObjectTypeInt:
		o.Object = &Int{Object: obj}
	case ObjectTypeEnum:
		o.Object = &Enum{Object: obj}
	default:
		return errors.New("invalid type")
	}
	o.XMLName = start.Name
	o.Type = ot
	if err := d.DecodeElement(o.Object, &start); err != nil {
		return err
	}
	// obj.Replace()
	return nil
}

// MarshalXML 实现xml序列化
func (o Obj) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	start.Name = o.XMLName
	if start.Name.Local == "" && o.Type != "" {
		start.Name.Local = string(o.Type)
	}
	o.Object.ReplaceAttr()
	return e.EncodeElement(o.Object, start)
}

type ObjOptions func(*Obj)

func WithNamespace(s string) ObjOptions {
	return func(o *Obj) {
		o.XMLName.Space = s
	}
}
